home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / tcl / tkstep0.3b3 / tkstep0 / tkstep / tkButton.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-07-08  |  59.6 KB  |  1,869 lines

  1. /* 
  2.  * tkButton.c --
  3.  *
  4.  *    This module implements a collection of button-like
  5.  *    widgets for the Tk toolkit.  The widgets implemented
  6.  *    include labels, buttons, check buttons, and radio
  7.  *    buttons.
  8.  *
  9.  * Copyright (c) 1990-1994 The Regents of the University of California.
  10.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  11.  *
  12.  * See the file "license.terms" for information on usage and redistribution
  13.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  14.  *
  15.  * SCCS: @(#) tkButton.c 1.128 96/03/01 17:34:49
  16.  */
  17. /*
  18.  * TkSTEP modifications Copyright by
  19.  * Alfredo K. Kojima
  20.  */
  21.  
  22.  
  23. #include "default.h"
  24. #include "tkPort.h"
  25. #include "tkInt.h"
  26.  
  27. /*
  28.  * A data structure of the following type is kept for each
  29.  * widget managed by this file:
  30.  */
  31.  
  32. typedef struct {
  33.     Tk_Window tkwin;        /* Window that embodies the button.  NULL
  34.                  * means that the window has been destroyed. */
  35.     Display *display;        /* Display containing widget.  Needed to
  36.                  * free up resources after tkwin is gone. */
  37.     Tcl_Interp *interp;        /* Interpreter associated with button. */
  38.     Tcl_Command widgetCmd;    /* Token for button's widget command. */
  39.     int type;            /* Type of widget:  restricts operations
  40.                  * that may be performed on widget.  See
  41.                  * below for possible values. */
  42.  
  43.     /*
  44.      * Information about what's in the button.
  45.      */
  46.  
  47.     char *text;            /* Text to display in button (malloc'ed)
  48.                  * or NULL. */
  49.     int numChars;        /* # of characters in text. */
  50.     int underline;        /* Index of character to underline.  < 0 means
  51.                  * don't underline anything. */
  52.     char *textVarName;        /* Name of variable (malloc'ed) or NULL.
  53.                  * If non-NULL, button displays the contents
  54.                  * of this variable. */
  55.     Pixmap bitmap;        /* Bitmap to display or None.  If not None
  56.                  * then text and textVar are ignored. */
  57.     char *imageString;        /* Name of image to display (malloc'ed), or
  58.                  * NULL.  If non-NULL, bitmap, text, and
  59.                  * textVarName are ignored. */
  60.     Tk_Image image;        /* Image to display in window, or NULL if
  61.                  * none. */
  62.     char *selectImageString;    /* Name of image to display when selected
  63.                  * (malloc'ed), or NULL. */
  64.     Tk_Image selectImage;    /* Image to display in window when selected,
  65.                  * or NULL if none.  Ignored if image is
  66.                  * NULL. */
  67.  
  68.     /*
  69.      * Information used when displaying widget:
  70.      */
  71.  
  72.     Tk_Uid state;        /* State of button for display purposes:
  73.                  * normal, active, or disabled. */
  74.     Tk_3DBorder normalBorder;    /* Structure used to draw 3-D
  75.                  * border and background when window
  76.                  * isn't active.  NULL means no such
  77.                  * border exists. */
  78.     Tk_3DBorder activeBorder;    /* Structure used to draw 3-D
  79.                  * border and background when window
  80.                  * is active.  NULL means no such
  81.                  * border exists. */
  82.     int borderWidth;        /* Width of border. */
  83.     int relief;            /* 3-d effect: TK_RELIEF_RAISED, etc. */
  84.     int highlightWidth;        /* Width in pixels of highlight to draw
  85.                  * around widget when it has the focus.
  86.                  * <= 0 means don't draw a highlight. */
  87.     XColor *highlightBgColorPtr;
  88.                 /* Color for drawing traversal highlight
  89.                  * area when highlight is off. */
  90.     XColor *highlightColorPtr;    /* Color for drawing traversal highlight. */
  91.     int inset;            /* Total width of all borders, including
  92.                  * traversal highlight and 3-D border.
  93.                  * Indicates how much interior stuff must
  94.                  * be offset from outside edges to leave
  95.                  * room for borders. */
  96.     XFontStruct *fontPtr;    /* Information about text font, or NULL. */
  97.     XColor *normalFg;        /* Foreground color in normal mode. */
  98.     XColor *activeFg;        /* Foreground color in active mode.  NULL
  99.                  * means use normalFg instead. */
  100.     XColor *disabledFg;        /* Foreground color when disabled.  NULL
  101.                  * means use normalFg with a 50% stipple
  102.                  * instead. */
  103.     GC normalTextGC;        /* GC for drawing text in normal mode.  Also
  104.                  * used to copy from off-screen pixmap onto
  105.                  * screen. */
  106.     GC activeTextGC;        /* GC for drawing text in active mode (NULL
  107.                  * means use normalTextGC). */
  108.     Pixmap gray;        /* Pixmap for displaying disabled text if
  109.                  * disabledFg is NULL. */
  110.     GC disabledGC;        /* Used to produce disabled effect.  If
  111.                  * disabledFg isn't NULL, this GC is used to
  112.                  * draw button text or icon.  Otherwise
  113.                  * text or icon is drawn with normalGC and
  114.                  * this GC is used to stipple background
  115.                  * across it.  For labels this is None. */
  116.     GC copyGC;            /* Used for copying information from an
  117.                  * off-screen pixmap to the screen. */
  118.     char *widthString;        /* Value of -width option.  Malloc'ed. */
  119.     char *heightString;        /* Value of -height option.  Malloc'ed. */
  120.     int width, height;        /* If > 0, these specify dimensions to request
  121.                  * for window, in characters for text and in
  122.                  * pixels for bitmaps.  In this case the actual
  123.                  * size of the text string or bitmap is
  124.                  * ignored in computing desired window size. */
  125.     int wrapLength;        /* Line length (in pixels) at which to wrap
  126.                  * onto next line.  <= 0 means don't wrap
  127.                  * except at newlines. */
  128.     int padX, padY;        /* Extra space around text (pixels to leave
  129.                  * on each side).  Ignored for bitmaps and
  130.                  * images. */
  131.     Tk_Anchor anchor;        /* Where text/bitmap should be displayed
  132.                  * inside button region. */
  133.     Tk_Justify justify;        /* Justification to use for multi-line text. */
  134.     int indicatorOn;        /* True means draw indicator, false means
  135.                  * don't draw it. */
  136.     Tk_3DBorder selectBorder;    /* For drawing indicator background, or perhaps
  137.                  * widget background, when selected. */
  138.     int textWidth;        /* Width needed to display text as requested,
  139.                  * in pixels. */
  140.     int textHeight;        /* Height needed to display text as requested,
  141.                  * in pixels. */
  142.     int indicatorSpace;        /* Horizontal space (in pixels) allocated for
  143.                  * display of indicator. */
  144.     int indicatorDiameter;    /* Diameter of indicator, in pixels. */
  145.  
  146.     /*
  147.      * For check and radio buttons, the fields below are used
  148.      * to manage the variable indicating the button's state.
  149.      */
  150.  
  151.     char *selVarName;        /* Name of variable used to control selected
  152.                  * state of button.  Malloc'ed (if
  153.                  * not NULL). */
  154.     char *onValue;        /* Value to store in variable when
  155.                  * this button is selected.  Malloc'ed (if
  156.                  * not NULL). */
  157.     char *offValue;        /* Value to store in variable when this
  158.                  * button isn't selected.  Malloc'ed
  159.                  * (if not NULL).  Valid only for check
  160.                  * buttons. */
  161.  
  162.     /*
  163.      * Miscellaneous information:
  164.      */
  165.  
  166.     Tk_Cursor cursor;        /* Current cursor for window, or None. */
  167.     char *takeFocus;        /* Value of -takefocus option;  not used in
  168.                  * the C code, but used by keyboard traversal
  169.                  * scripts.  Malloc'ed, but may be NULL. */
  170.     char *command;        /* Command to execute when button is
  171.                  * invoked; valid for buttons only.
  172.                  * If not NULL, it's malloc-ed. */
  173.     int flags;            /* Various flags;  see below for
  174.                  * definitions. */
  175. } Button;
  176.  
  177. /*
  178.  * Possible "type" values for buttons.  These are the kinds of
  179.  * widgets supported by this file.  The ordering of the type
  180.  * numbers is significant:  greater means more features and is
  181.  * used in the code.
  182.  */
  183.  
  184. #define TYPE_LABEL        0
  185. #define TYPE_BUTTON        1
  186. #define TYPE_CHECK_BUTTON    2
  187. #define TYPE_RADIO_BUTTON    3
  188.  
  189. /*
  190.  * Class names for buttons, indexed by one of the type values above.
  191.  */
  192.  
  193. static char *classNames[] = {"Label", "Button", "Checkbutton", "Radiobutton"};
  194.  
  195. /*
  196.  * Flag bits for buttons:
  197.  *
  198.  * REDRAW_PENDING:        Non-zero means a DoWhenIdle handler
  199.  *                has already been queued to redraw
  200.  *                this window.
  201.  * SELECTED:            Non-zero means this button is selected,
  202.  *                so special highlight should be drawn.
  203.  * GOT_FOCUS:            Non-zero means this button currently
  204.  *                has the input focus.
  205.  */
  206.  
  207. #define REDRAW_PENDING        1
  208. #define SELECTED        2
  209. #define GOT_FOCUS        4
  210.  
  211. /*
  212.  * Mask values used to selectively enable entries in the
  213.  * configuration specs:
  214.  */
  215.  
  216. #define LABEL_MASK        TK_CONFIG_USER_BIT
  217. #define BUTTON_MASK        TK_CONFIG_USER_BIT << 1
  218. #define CHECK_BUTTON_MASK    TK_CONFIG_USER_BIT << 2
  219. #define RADIO_BUTTON_MASK    TK_CONFIG_USER_BIT << 3
  220. #define ALL_MASK        (LABEL_MASK | BUTTON_MASK \
  221.     | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK)
  222.  
  223.  
  224. #define CHECK_WIDTH    10
  225. #define CHECK_HEIGHT    10
  226.  
  227. static int configFlags[] = {LABEL_MASK, BUTTON_MASK,
  228.     CHECK_BUTTON_MASK, RADIO_BUTTON_MASK};
  229.  
  230. /*
  231.  * Information used for parsing configuration specs:
  232.  */
  233.  
  234. static Tk_ConfigSpec configSpecs[] = {
  235.     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
  236.     DEF_BUTTON_ACTIVE_BG_COLOR, Tk_Offset(Button, activeBorder),
  237.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
  238.     |TK_CONFIG_COLOR_ONLY},
  239.     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
  240.     DEF_BUTTON_ACTIVE_BG_MONO, Tk_Offset(Button, activeBorder),
  241.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
  242.     |TK_CONFIG_MONO_ONLY},
  243.     {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
  244.     DEF_BUTTON_ACTIVE_FG_COLOR, Tk_Offset(Button, activeFg), 
  245.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
  246.     |TK_CONFIG_COLOR_ONLY},
  247.     {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
  248.     DEF_BUTTON_ACTIVE_FG_MONO, Tk_Offset(Button, activeFg), 
  249.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
  250.     |TK_CONFIG_MONO_ONLY},
  251.     {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
  252.     DEF_BUTTON_ANCHOR, Tk_Offset(Button, anchor), ALL_MASK},
  253.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  254.     DEF_BUTTON_BG_COLOR, Tk_Offset(Button, normalBorder),
  255.     ALL_MASK | TK_CONFIG_COLOR_ONLY},
  256.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  257.     DEF_BUTTON_BG_MONO, Tk_Offset(Button, normalBorder),
  258.     ALL_MASK | TK_CONFIG_MONO_ONLY},
  259.     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
  260.     (char *) NULL, 0, ALL_MASK},
  261.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
  262.     (char *) NULL, 0, ALL_MASK},
  263.     {TK_CONFIG_BITMAP, "-bitmap", "bitmap", "Bitmap",
  264.     DEF_BUTTON_BITMAP, Tk_Offset(Button, bitmap),
  265.     ALL_MASK|TK_CONFIG_NULL_OK},
  266.     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
  267.     DEF_BUTTON_BORDER_WIDTH, Tk_Offset(Button, borderWidth), ALL_MASK},
  268.     {TK_CONFIG_STRING, "-command", "command", "Command",
  269.     DEF_BUTTON_COMMAND, Tk_Offset(Button, command),
  270.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_NULL_OK},
  271.     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
  272.     DEF_BUTTON_CURSOR, Tk_Offset(Button, cursor),
  273.     ALL_MASK|TK_CONFIG_NULL_OK},
  274.     {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
  275.     "DisabledForeground", DEF_BUTTON_DISABLED_FG_COLOR,
  276.     Tk_Offset(Button, disabledFg), BUTTON_MASK|CHECK_BUTTON_MASK
  277.     |RADIO_BUTTON_MASK|TK_CONFIG_COLOR_ONLY|TK_CONFIG_NULL_OK},
  278.     {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
  279.     "DisabledForeground", DEF_BUTTON_DISABLED_FG_MONO,
  280.     Tk_Offset(Button, disabledFg), BUTTON_MASK|CHECK_BUTTON_MASK
  281.     |RADIO_BUTTON_MASK|TK_CONFIG_MONO_ONLY|TK_CONFIG_NULL_OK},
  282.     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
  283.     (char *) NULL, 0, ALL_MASK},
  284.     {TK_CONFIG_FONT, "-font", "font", "Font",
  285.     DEF_BUTTON_FONT, Tk_Offset(Button, fontPtr),
  286.     ALL_MASK},
  287.     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
  288.     DEF_BUTTON_FG, Tk_Offset(Button, normalFg), ALL_MASK},
  289.     {TK_CONFIG_STRING, "-height", "height", "Height",
  290.     DEF_BUTTON_HEIGHT, Tk_Offset(Button, heightString), ALL_MASK},
  291.     {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
  292.     "HighlightBackground", DEF_BUTTON_HIGHLIGHT_BG,
  293.     Tk_Offset(Button, highlightBgColorPtr), ALL_MASK},
  294.     {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
  295.     DEF_BUTTON_HIGHLIGHT, Tk_Offset(Button, highlightColorPtr),
  296.     ALL_MASK},
  297.     {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
  298.     "HighlightThickness",
  299.     DEF_LABEL_HIGHLIGHT_WIDTH, Tk_Offset(Button, highlightWidth),
  300.     LABEL_MASK},
  301.     {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
  302.     "HighlightThickness",
  303.     DEF_BUTTON_HIGHLIGHT_WIDTH, Tk_Offset(Button, highlightWidth),
  304.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
  305.     {TK_CONFIG_STRING, "-image", "image", "Image",
  306.     DEF_BUTTON_IMAGE, Tk_Offset(Button, imageString),
  307.     ALL_MASK|TK_CONFIG_NULL_OK},
  308.     {TK_CONFIG_BOOLEAN, "-indicatoron", "indicatorOn", "IndicatorOn",
  309.     DEF_BUTTON_INDICATOR, Tk_Offset(Button, indicatorOn),
  310.     CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
  311.     {TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
  312.     DEF_BUTTON_JUSTIFY, Tk_Offset(Button, justify), ALL_MASK},
  313.     {TK_CONFIG_STRING, "-offvalue", "offValue", "Value",
  314.     DEF_BUTTON_OFF_VALUE, Tk_Offset(Button, offValue),
  315.     CHECK_BUTTON_MASK},
  316.     {TK_CONFIG_STRING, "-onvalue", "onValue", "Value",
  317.     DEF_BUTTON_ON_VALUE, Tk_Offset(Button, onValue),
  318.     CHECK_BUTTON_MASK},
  319.     {TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
  320.     DEF_BUTTON_PADX, Tk_Offset(Button, padX), BUTTON_MASK},
  321.     {TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
  322.     DEF_LABCHKRAD_PADX, Tk_Offset(Button, padX),
  323.     LABEL_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
  324.     {TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
  325.     DEF_BUTTON_PADY, Tk_Offset(Button, padY), BUTTON_MASK},
  326.     {TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
  327.     DEF_LABCHKRAD_PADY, Tk_Offset(Button, padY),
  328.     LABEL_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
  329.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  330.     DEF_BUTTON_RELIEF, Tk_Offset(Button, relief), BUTTON_MASK},
  331.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  332.     DEF_LABCHKRAD_RELIEF, Tk_Offset(Button, relief),
  333.     LABEL_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
  334.     {TK_CONFIG_BORDER, "-selectcolor", "selectColor", "Background",
  335.     DEF_BUTTON_SELECT_COLOR, Tk_Offset(Button, selectBorder),
  336.     CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_COLOR_ONLY
  337.     |TK_CONFIG_NULL_OK},
  338.     {TK_CONFIG_BORDER, "-selectcolor", "selectColor", "Background",
  339.     DEF_BUTTON_SELECT_MONO, Tk_Offset(Button, selectBorder),
  340.     CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_MONO_ONLY
  341.     |TK_CONFIG_NULL_OK},
  342.     {TK_CONFIG_STRING, "-selectimage", "selectImage", "SelectImage",
  343.     DEF_BUTTON_SELECT_IMAGE, Tk_Offset(Button, selectImageString),
  344.     CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_NULL_OK},
  345.     {TK_CONFIG_UID, "-state", "state", "State",
  346.     DEF_BUTTON_STATE, Tk_Offset(Button, state),
  347.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
  348.     {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
  349.     DEF_LABEL_TAKE_FOCUS, Tk_Offset(Button, takeFocus),
  350.     LABEL_MASK|TK_CONFIG_NULL_OK},
  351.     {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
  352.     DEF_BUTTON_TAKE_FOCUS, Tk_Offset(Button, takeFocus),
  353.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_NULL_OK},
  354.     {TK_CONFIG_STRING, "-text", "text", "Text",
  355.     DEF_BUTTON_TEXT, Tk_Offset(Button, text), ALL_MASK},
  356.     {TK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
  357.     DEF_BUTTON_TEXT_VARIABLE, Tk_Offset(Button, textVarName),
  358.     ALL_MASK|TK_CONFIG_NULL_OK},
  359.     {TK_CONFIG_INT, "-underline", "underline", "Underline",
  360.     DEF_BUTTON_UNDERLINE, Tk_Offset(Button, underline), ALL_MASK},
  361.     {TK_CONFIG_STRING, "-value", "value", "Value",
  362.     DEF_BUTTON_VALUE, Tk_Offset(Button, onValue),
  363.     RADIO_BUTTON_MASK},
  364.     {TK_CONFIG_STRING, "-variable", "variable", "Variable",
  365.     DEF_RADIOBUTTON_VARIABLE, Tk_Offset(Button, selVarName),
  366.     RADIO_BUTTON_MASK},
  367.     {TK_CONFIG_STRING, "-variable", "variable", "Variable",
  368.     DEF_CHECKBUTTON_VARIABLE, Tk_Offset(Button, selVarName),
  369.     CHECK_BUTTON_MASK|TK_CONFIG_NULL_OK},
  370.     {TK_CONFIG_STRING, "-width", "width", "Width",
  371.     DEF_BUTTON_WIDTH, Tk_Offset(Button, widthString), ALL_MASK},
  372.     {TK_CONFIG_PIXELS, "-wraplength", "wrapLength", "WrapLength",
  373.     DEF_BUTTON_WRAP_LENGTH, Tk_Offset(Button, wrapLength), ALL_MASK},
  374.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  375.     (char *) NULL, 0, 0}
  376. };
  377.  
  378. /*
  379.  * String to print out in error messages, identifying options for
  380.  * widget commands for different types of labels or buttons:
  381.  */
  382.  
  383. static char *optionStrings[] = {
  384.     "cget or configure",
  385.     "cget, configure, flash, or invoke",
  386.     "cget, configure, deselect, flash, invoke, select, or toggle",
  387.     "cget, configure, deselect, flash, invoke, or select"
  388. };
  389.  
  390. /*
  391.  * Forward declarations for procedures defined later in this file:
  392.  */
  393.  
  394. static void        ButtonCmdDeletedProc _ANSI_ARGS_((
  395.                 ClientData clientData));
  396. static int        ButtonCreate _ANSI_ARGS_((ClientData clientData,
  397.                 Tcl_Interp *interp, int argc, char **argv,
  398.                 int type));
  399. static void        ButtonEventProc _ANSI_ARGS_((ClientData clientData,
  400.                 XEvent *eventPtr));
  401. static void        ButtonImageProc _ANSI_ARGS_((ClientData clientData,
  402.                 int x, int y, int width, int height,
  403.                 int imgWidth, int imgHeight));
  404. static void        ButtonSelectImageProc _ANSI_ARGS_((
  405.                 ClientData clientData, int x, int y, int width,
  406.                 int height, int imgWidth, int imgHeight));
  407. static char *        ButtonTextVarProc _ANSI_ARGS_((ClientData clientData,
  408.                 Tcl_Interp *interp, char *name1, char *name2,
  409.                 int flags));
  410. static char *        ButtonVarProc _ANSI_ARGS_((ClientData clientData,
  411.                 Tcl_Interp *interp, char *name1, char *name2,
  412.                 int flags));
  413. static int        ButtonWidgetCmd _ANSI_ARGS_((ClientData clientData,
  414.                 Tcl_Interp *interp, int argc, char **argv));
  415. static void        ComputeButtonGeometry _ANSI_ARGS_((Button *butPtr));
  416. static int        ConfigureButton _ANSI_ARGS_((Tcl_Interp *interp,
  417.                 Button *butPtr, int argc, char **argv,
  418.                 int flags));
  419. static void        DestroyButton _ANSI_ARGS_((Button *butPtr));
  420. static void        DisplayButton _ANSI_ARGS_((ClientData clientData));
  421. static int        InvokeButton  _ANSI_ARGS_((Button *butPtr));
  422.  
  423. /*
  424.  *--------------------------------------------------------------
  425.  *
  426.  * Tk_ButtonCmd, Tk_CheckbuttonCmd, Tk_LabelCmd, Tk_RadiobuttonCmd --
  427.  *
  428.  *    These procedures are invoked to process the "button", "label",
  429.  *    "radiobutton", and "checkbutton" Tcl commands.  See the
  430.  *    user documentation for details on what they do.
  431.  *
  432.  * Results:
  433.  *    A standard Tcl result.
  434.  *
  435.  * Side effects:
  436.  *    See the user documentation.  These procedures are just wrappers;
  437.  *    they call ButtonCreate to do all of the real work.
  438.  *
  439.  *--------------------------------------------------------------
  440.  */
  441.  
  442. int
  443. Tk_ButtonCmd(clientData, interp, argc, argv)
  444.     ClientData clientData;    /* Main window associated with
  445.                  * interpreter. */
  446.     Tcl_Interp *interp;        /* Current interpreter. */
  447.     int argc;            /* Number of arguments. */
  448.     char **argv;        /* Argument strings. */
  449. {
  450.     return ButtonCreate(clientData, interp, argc, argv, TYPE_BUTTON);
  451. }
  452.  
  453. int
  454. Tk_CheckbuttonCmd(clientData, interp, argc, argv)
  455.     ClientData clientData;    /* Main window associated with
  456.                  * interpreter. */
  457.     Tcl_Interp *interp;        /* Current interpreter. */
  458.     int argc;            /* Number of arguments. */
  459.     char **argv;        /* Argument strings. */
  460. {
  461.     return ButtonCreate(clientData, interp, argc, argv, TYPE_CHECK_BUTTON);
  462. }
  463.  
  464. int
  465. Tk_LabelCmd(clientData, interp, argc, argv)
  466.     ClientData clientData;    /* Main window associated with
  467.                  * interpreter. */
  468.     Tcl_Interp *interp;        /* Current interpreter. */
  469.     int argc;            /* Number of arguments. */
  470.     char **argv;        /* Argument strings. */
  471. {
  472.     return ButtonCreate(clientData, interp, argc, argv, TYPE_LABEL);
  473. }
  474.  
  475. int
  476. Tk_RadiobuttonCmd(clientData, interp, argc, argv)
  477.     ClientData clientData;    /* Main window associated with
  478.                  * interpreter. */
  479.     Tcl_Interp *interp;        /* Current interpreter. */
  480.     int argc;            /* Number of arguments. */
  481.     char **argv;        /* Argument strings. */
  482. {
  483.     return ButtonCreate(clientData, interp, argc, argv, TYPE_RADIO_BUTTON);
  484. }
  485.  
  486. /*
  487.  *--------------------------------------------------------------
  488.  *
  489.  * ButtonCreate --
  490.  *
  491.  *    This procedure does all the real work of implementing the
  492.  *    "button", "label", "radiobutton", and "checkbutton" Tcl
  493.  *    commands.  See the user documentation for details on what it does.
  494.  *
  495.  * Results:
  496.  *    A standard Tcl result.
  497.  *
  498.  * Side effects:
  499.  *    See the user documentation.
  500.  *
  501.  *--------------------------------------------------------------
  502.  */
  503.  
  504. static int
  505. ButtonCreate(clientData, interp, argc, argv, type)
  506.     ClientData clientData;    /* Main window associated with
  507.                  * interpreter. */
  508.     Tcl_Interp *interp;        /* Current interpreter. */
  509.     int argc;            /* Number of arguments. */
  510.     char **argv;        /* Argument strings. */
  511.     int type;            /* Type of button to create: TYPE_LABEL,
  512.                  * TYPE_BUTTON, TYPE_CHECK_BUTTON, or
  513.                  * TYPE_RADIO_BUTTON. */
  514. {
  515.     register Button *butPtr;
  516.     Tk_Window tkwin = (Tk_Window) clientData;
  517.     Tk_Window new;
  518.  
  519.     if (argc < 2) {
  520.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  521.         argv[0], " pathName ?options?\"", (char *) NULL);
  522.     return TCL_ERROR;
  523.     }
  524.  
  525.     /*
  526.      * Create the new window.
  527.      */
  528.  
  529.     new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
  530.     if (new == NULL) {
  531.     return TCL_ERROR;
  532.     }
  533.  
  534.     /*
  535.      * Initialize the data structure for the button.
  536.      */
  537.  
  538.     butPtr = (Button *) ckalloc(sizeof(Button));
  539.     butPtr->tkwin = new;
  540.     butPtr->display = Tk_Display(new);
  541.     butPtr->widgetCmd = Tcl_CreateCommand(interp, Tk_PathName(butPtr->tkwin),
  542.         ButtonWidgetCmd, (ClientData) butPtr, ButtonCmdDeletedProc);
  543.     butPtr->interp = interp;
  544.     butPtr->type = type;
  545.     butPtr->text = NULL;
  546.     butPtr->numChars = 0;
  547.     butPtr->underline = -1;
  548.     butPtr->textVarName = NULL;
  549.     butPtr->bitmap = None;
  550.     butPtr->imageString = NULL;
  551.     butPtr->image = NULL;
  552.     butPtr->selectImageString = NULL;
  553.     butPtr->selectImage = NULL;
  554.     butPtr->state = tkNormalUid;
  555.     butPtr->normalBorder = NULL;
  556.     butPtr->activeBorder = NULL;
  557.     butPtr->borderWidth = 0;
  558.     butPtr->relief = TK_RELIEF_FLAT;
  559.     butPtr->highlightWidth = 0;
  560.     butPtr->highlightBgColorPtr = NULL;
  561.     butPtr->highlightColorPtr = NULL;
  562.     butPtr->inset = 0;
  563.     butPtr->fontPtr = NULL;
  564.     butPtr->normalFg = NULL;
  565.     butPtr->activeFg = NULL;
  566.     butPtr->disabledFg = NULL;
  567.     butPtr->normalTextGC = None;
  568.     butPtr->activeTextGC = None;
  569.     butPtr->gray = None;
  570.     butPtr->disabledGC = None;
  571.     butPtr->copyGC = None;
  572.     butPtr->widthString = NULL;
  573.     butPtr->heightString = NULL;
  574.     butPtr->width = 0;
  575.     butPtr->height = 0;
  576.     butPtr->wrapLength = 0;
  577.     butPtr->padX = 0;
  578.     butPtr->padY = 0;
  579.     butPtr->anchor = TK_ANCHOR_CENTER;
  580.     butPtr->justify = TK_JUSTIFY_CENTER;
  581.     butPtr->indicatorOn = 0;
  582.     butPtr->selectBorder = NULL;
  583.     butPtr->indicatorSpace = 0;
  584.     butPtr->indicatorDiameter = 0;
  585.     butPtr->selVarName = NULL;
  586.     butPtr->onValue = NULL;
  587.     butPtr->offValue = NULL;
  588.     butPtr->cursor = None;
  589.     butPtr->command = NULL;
  590.     butPtr->takeFocus = NULL;
  591.     butPtr->flags = 0;
  592.  
  593.     Tk_SetClass(new, classNames[type]);
  594.     Tk_CreateEventHandler(butPtr->tkwin,
  595.         ExposureMask|StructureNotifyMask|FocusChangeMask,
  596.         ButtonEventProc, (ClientData) butPtr);
  597.     if (ConfigureButton(interp, butPtr, argc-2, argv+2,
  598.         configFlags[type]) != TCL_OK) {
  599.     Tk_DestroyWindow(butPtr->tkwin);
  600.     return TCL_ERROR;
  601.     }
  602.  
  603.     interp->result = Tk_PathName(butPtr->tkwin);
  604.     return TCL_OK;
  605. }
  606.  
  607. /*
  608.  *--------------------------------------------------------------
  609.  *
  610.  * ButtonWidgetCmd --
  611.  *
  612.  *    This procedure is invoked to process the Tcl command
  613.  *    that corresponds to a widget managed by this module.
  614.  *    See the user documentation for details on what it does.
  615.  *
  616.  * Results:
  617.  *    A standard Tcl result.
  618.  *
  619.  * Side effects:
  620.  *    See the user documentation.
  621.  *
  622.  *--------------------------------------------------------------
  623.  */
  624.  
  625. static int
  626. ButtonWidgetCmd(clientData, interp, argc, argv)
  627.     ClientData clientData;    /* Information about button widget. */
  628.     Tcl_Interp *interp;        /* Current interpreter. */
  629.     int argc;            /* Number of arguments. */
  630.     char **argv;        /* Argument strings. */
  631. {
  632.     register Button *butPtr = (Button *) clientData;
  633.     int result = TCL_OK;
  634.     size_t length;
  635.     int c;
  636.  
  637.     if (argc < 2) {
  638.     sprintf(interp->result,
  639.         "wrong # args: should be \"%.50s option ?arg arg ...?\"",
  640.         argv[0]);
  641.     return TCL_ERROR;
  642.     }
  643.     Tcl_Preserve((ClientData) butPtr);
  644.     c = argv[1][0];
  645.     length = strlen(argv[1]);
  646.     if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
  647.         && (length >= 2)) {
  648.     if (argc != 3) {
  649.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  650.             argv[0], " cget option\"",
  651.             (char *) NULL);
  652.         goto error;
  653.     }
  654.     result = Tk_ConfigureValue(interp, butPtr->tkwin, configSpecs,
  655.         (char *) butPtr, argv[2], configFlags[butPtr->type]);
  656.     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
  657.         && (length >= 2)) {
  658.     if (argc == 2) {
  659.         result = Tk_ConfigureInfo(interp, butPtr->tkwin, configSpecs,
  660.             (char *) butPtr, (char *) NULL, configFlags[butPtr->type]);
  661.     } else if (argc == 3) {
  662.         result = Tk_ConfigureInfo(interp, butPtr->tkwin, configSpecs,
  663.             (char *) butPtr, argv[2],
  664.             configFlags[butPtr->type]);
  665.     } else {
  666.         result = ConfigureButton(interp, butPtr, argc-2, argv+2,
  667.             configFlags[butPtr->type] | TK_CONFIG_ARGV_ONLY);
  668.     }
  669.     } else if ((c == 'd') && (strncmp(argv[1], "deselect", length) == 0)
  670.         && (butPtr->type >= TYPE_CHECK_BUTTON)) {
  671.     if (argc > 2) {
  672.         sprintf(interp->result,
  673.             "wrong # args: should be \"%.50s deselect\"",
  674.             argv[0]);
  675.         goto error;
  676.     }
  677.     if (butPtr->type == TYPE_CHECK_BUTTON) {
  678.         if (Tcl_SetVar(interp, butPtr->selVarName, butPtr->offValue,
  679.             TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  680.         result = TCL_ERROR;
  681.         }
  682.     } else if (butPtr->flags & SELECTED) {
  683.         if (Tcl_SetVar(interp, butPtr->selVarName, "",
  684.             TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  685.         result = TCL_ERROR;
  686.         };
  687.     }
  688.     } else if ((c == 'f') && (strncmp(argv[1], "flash", length) == 0)
  689.         && (butPtr->type != TYPE_LABEL)) {
  690.     int i;
  691.  
  692.     if (argc > 2) {
  693.         sprintf(interp->result,
  694.             "wrong # args: should be \"%.50s flash\"",
  695.             argv[0]);
  696.         goto error;
  697.     }
  698.     if (butPtr->state != tkDisabledUid) {
  699.         for (i = 0; i < 4; i++) {
  700.         butPtr->state = (butPtr->state == tkNormalUid)
  701.             ? tkActiveUid : tkNormalUid;
  702.         Tk_SetBackgroundFromBorder(butPtr->tkwin,
  703.             (butPtr->state == tkActiveUid) ? butPtr->activeBorder
  704.             : butPtr->normalBorder);
  705.         DisplayButton((ClientData) butPtr);
  706.  
  707.         /*
  708.          * Special note: must cancel any existing idle handler
  709.          * for DisplayButton;  it's no longer needed, and DisplayButton
  710.          * cleared the REDRAW_PENDING flag.
  711.          */
  712.  
  713.         Tcl_CancelIdleCall(DisplayButton, (ClientData) butPtr);
  714.         XFlush(butPtr->display);
  715.         Tcl_Sleep(50);
  716.         }
  717.     }
  718.     } else if ((c == 'i') && (strncmp(argv[1], "invoke", length) == 0)
  719.         && (butPtr->type > TYPE_LABEL)) {
  720.     if (argc > 2) {
  721.         sprintf(interp->result,
  722.             "wrong # args: should be \"%.50s invoke\"",
  723.             argv[0]);
  724.         goto error;
  725.     }
  726.     if (butPtr->state != tkDisabledUid) {
  727.         result = InvokeButton(butPtr);
  728.     }
  729.     } else if ((c == 's') && (strncmp(argv[1], "select", length) == 0)
  730.         && (butPtr->type >= TYPE_CHECK_BUTTON)) {
  731.     if (argc > 2) {
  732.         sprintf(interp->result,
  733.             "wrong # args: should be \"%.50s select\"",
  734.             argv[0]);
  735.         goto error;
  736.     }
  737.     if (Tcl_SetVar(interp, butPtr->selVarName, butPtr->onValue,
  738.         TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  739.         result = TCL_ERROR;
  740.     }
  741.     } else if ((c == 't') && (strncmp(argv[1], "toggle", length) == 0)
  742.         && (length >= 2) && (butPtr->type == TYPE_CHECK_BUTTON)) {
  743.     if (argc > 2) {
  744.         sprintf(interp->result,
  745.             "wrong # args: should be \"%.50s toggle\"",
  746.             argv[0]);
  747.         goto error;
  748.     }
  749.     if (butPtr->flags & SELECTED) {
  750.         if (Tcl_SetVar(interp, butPtr->selVarName, butPtr->offValue,
  751.             TCL_GLOBAL_ONLY) == NULL) {
  752.         result = TCL_ERROR;
  753.         }
  754.     } else {
  755.         if (Tcl_SetVar(interp, butPtr->selVarName, butPtr->onValue,
  756.             TCL_GLOBAL_ONLY) == NULL) {
  757.         result = TCL_ERROR;
  758.         }
  759.     }
  760.     } else {
  761.     sprintf(interp->result,
  762.         "bad option \"%.50s\": must be %s", argv[1],
  763.         optionStrings[butPtr->type]);
  764.     goto error;
  765.     }
  766.     Tcl_Release((ClientData) butPtr);
  767.     return result;
  768.  
  769.     error:
  770.     Tcl_Release((ClientData) butPtr);
  771.     return TCL_ERROR;
  772. }
  773.  
  774. /*
  775.  *----------------------------------------------------------------------
  776.  *
  777.  * DestroyButton --
  778.  *
  779.  *    This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
  780.  *    to clean up the internal structure of a button at a safe time
  781.  *    (when no-one is using it anymore).
  782.  *
  783.  * Results:
  784.  *    None.
  785.  *
  786.  * Side effects:
  787.  *    Everything associated with the widget is freed up.
  788.  *
  789.  *----------------------------------------------------------------------
  790.  */
  791.  
  792. static void
  793. DestroyButton(butPtr)
  794.     Button *butPtr;        /* Info about button widget. */
  795. {
  796.     /*
  797.      * Free up all the stuff that requires special handling, then
  798.      * let Tk_FreeOptions handle all the standard option-related
  799.      * stuff.
  800.      */
  801.  
  802.     if (butPtr->textVarName != NULL) {
  803.     Tcl_UntraceVar(butPtr->interp, butPtr->textVarName,
  804.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  805.         ButtonTextVarProc, (ClientData) butPtr);
  806.     }
  807.     if (butPtr->image != NULL) {
  808.     Tk_FreeImage(butPtr->image);
  809.     }
  810.     if (butPtr->selectImage != NULL) {
  811.     Tk_FreeImage(butPtr->selectImage);
  812.     }
  813.     if (butPtr->normalTextGC != None) {
  814.     Tk_FreeGC(butPtr->display, butPtr->normalTextGC);
  815.     }
  816.     if (butPtr->activeTextGC != None) {
  817.     Tk_FreeGC(butPtr->display, butPtr->activeTextGC);
  818.     }
  819.     if (butPtr->gray != None) {
  820.     Tk_FreeBitmap(butPtr->display, butPtr->gray);
  821.     }
  822.     if (butPtr->disabledGC != None) {
  823.     Tk_FreeGC(butPtr->display, butPtr->disabledGC);
  824.     }
  825.     if (butPtr->copyGC != None) {
  826.     Tk_FreeGC(butPtr->display, butPtr->copyGC);
  827.     }
  828.     if (butPtr->selVarName != NULL) {
  829.     Tcl_UntraceVar(butPtr->interp, butPtr->selVarName,
  830.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  831.         ButtonVarProc, (ClientData) butPtr);
  832.     }
  833.     Tk_FreeOptions(configSpecs, (char *) butPtr, butPtr->display,
  834.         configFlags[butPtr->type]);
  835.     Tcl_EventuallyFree((ClientData)butPtr, TCL_DYNAMIC);
  836. }
  837.  
  838. /*
  839.  *----------------------------------------------------------------------
  840.  *
  841.  * ConfigureButton --
  842.  *
  843.  *    This procedure is called to process an argv/argc list, plus
  844.  *    the Tk option database, in order to configure (or
  845.  *    reconfigure) a button widget.
  846.  *
  847.  * Results:
  848.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  849.  *    returned, then interp->result contains an error message.
  850.  *
  851.  * Side effects:
  852.  *    Configuration information, such as text string, colors, font,
  853.  *    etc. get set for butPtr;  old resources get freed, if there
  854.  *    were any.  The button is redisplayed.
  855.  *
  856.  *----------------------------------------------------------------------
  857.  */
  858.  
  859. static int
  860. ConfigureButton(interp, butPtr, argc, argv, flags)
  861.     Tcl_Interp *interp;        /* Used for error reporting. */
  862.     register Button *butPtr;    /* Information about widget;  may or may
  863.                  * not already have values for some fields. */
  864.     int argc;            /* Number of valid entries in argv. */
  865.     char **argv;        /* Arguments. */
  866.     int flags;            /* Flags to pass to Tk_ConfigureWidget. */
  867. {
  868.     XGCValues gcValues;
  869.     GC newGC;
  870.     unsigned long mask;
  871.     Tk_Image image;
  872.  
  873.     /*
  874.      * Eliminate any existing trace on variables monitored by the button.
  875.      */
  876.  
  877.     if (butPtr->textVarName != NULL) {
  878.     Tcl_UntraceVar(interp, butPtr->textVarName, 
  879.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  880.         ButtonTextVarProc, (ClientData) butPtr);
  881.     }
  882.     if (butPtr->selVarName != NULL) {
  883.     Tcl_UntraceVar(interp, butPtr->selVarName, 
  884.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  885.         ButtonVarProc, (ClientData) butPtr);
  886.     }
  887.  
  888.     if (Tk_ConfigureWidget(interp, butPtr->tkwin, configSpecs,
  889.         argc, argv, (char *) butPtr, flags) != TCL_OK) {
  890.     return TCL_ERROR;
  891.     }
  892.  
  893.     /*
  894.      * A few options need special processing, such as setting the
  895.      * background from a 3-D border, or filling in complicated
  896.      * defaults that couldn't be specified to Tk_ConfigureWidget.
  897.      */
  898.  
  899.     if ((butPtr->state == tkActiveUid) && !Tk_StrictMotif(butPtr->tkwin)) {
  900.     Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->activeBorder);
  901.     } else {
  902.     Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->normalBorder);
  903.     if ((butPtr->state != tkNormalUid) && (butPtr->state != tkActiveUid)
  904.         && (butPtr->state != tkDisabledUid)) {
  905.         Tcl_AppendResult(interp, "bad state value \"", butPtr->state,
  906.             "\": must be normal, active, or disabled", (char *) NULL);
  907.         butPtr->state = tkNormalUid;
  908.         return TCL_ERROR;
  909.     }
  910.     }
  911.  
  912.     if (butPtr->highlightWidth < 0) {
  913.     butPtr->highlightWidth = 0;
  914.     }
  915.  
  916.     gcValues.font = butPtr->fontPtr->fid;
  917.     gcValues.foreground = butPtr->normalFg->pixel;
  918.     gcValues.background = Tk_3DBorderColor(butPtr->normalBorder)->pixel;
  919.     
  920.     /*
  921.      * Note: GraphicsExpose events are disabled in normalTextGC because it's
  922.      * used to copy stuff from an off-screen pixmap onto the screen (we know
  923.      * that there's no problem with obscured areas).
  924.      */
  925.  
  926.     gcValues.graphics_exposures = False;
  927.     newGC = Tk_GetGC(butPtr->tkwin,
  928.         GCForeground|GCBackground|GCFont|GCGraphicsExposures,
  929.         &gcValues);
  930.     if (butPtr->normalTextGC != None) {
  931.     Tk_FreeGC(butPtr->display, butPtr->normalTextGC);
  932.     }
  933.     butPtr->normalTextGC = newGC;
  934.  
  935.     if (butPtr->activeFg != NULL) {
  936.     gcValues.font = butPtr->fontPtr->fid;
  937.     gcValues.foreground = butPtr->activeFg->pixel;
  938.     gcValues.background = Tk_3DBorderColor(butPtr->activeBorder)->pixel;
  939.     newGC = Tk_GetGC(butPtr->tkwin,
  940.         GCForeground|GCBackground|GCFont, &gcValues);
  941.     if (butPtr->activeTextGC != None) {
  942.         Tk_FreeGC(butPtr->display, butPtr->activeTextGC);
  943.     }
  944.     butPtr->activeTextGC = newGC;
  945.     }
  946.  
  947.     if (butPtr->type != TYPE_LABEL) {
  948.     gcValues.font = butPtr->fontPtr-> fid;
  949.     gcValues.background = Tk_3DBorderColor(butPtr->normalBorder)->pixel;
  950.     if ((butPtr->disabledFg != NULL) && (butPtr->imageString == NULL)) {
  951.         gcValues.foreground = butPtr->disabledFg->pixel;
  952.         mask = GCForeground|GCBackground|GCFont;
  953.     } else {
  954.         gcValues.foreground = gcValues.background;
  955.         if (butPtr->gray == None) {
  956.         butPtr->gray = Tk_GetBitmap(interp, butPtr->tkwin,
  957.             Tk_GetUid("gray50"));
  958.         if (butPtr->gray == None) {
  959.             return TCL_ERROR;
  960.         }
  961.         }
  962.         gcValues.fill_style = FillStippled;
  963.         gcValues.stipple = butPtr->gray;
  964.         mask = GCForeground|GCFillStyle|GCStipple;
  965.     }
  966.     newGC = Tk_GetGC(butPtr->tkwin, mask, &gcValues);
  967.     if (butPtr->disabledGC != None) {
  968.         Tk_FreeGC(butPtr->display, butPtr->disabledGC);
  969.     }
  970.     butPtr->disabledGC = newGC;
  971.     }
  972.  
  973.     if (butPtr->copyGC == None) {
  974.     butPtr->copyGC = Tk_GetGC(butPtr->tkwin, 0, &gcValues);
  975.     }
  976.  
  977.     if (butPtr->padX < 0) {
  978.     butPtr->padX = 0;
  979.     }
  980.     if (butPtr->padY < 0) {
  981.     butPtr->padY = 0;
  982.     }
  983.  
  984.     if (butPtr->type >= TYPE_CHECK_BUTTON) {
  985.     char *value;
  986.  
  987.     if (butPtr->selVarName == NULL) {
  988.         butPtr->selVarName = (char *) ckalloc((unsigned)
  989.             (strlen(Tk_Name(butPtr->tkwin)) + 1));
  990.         strcpy(butPtr->selVarName, Tk_Name(butPtr->tkwin));
  991.     }
  992.  
  993.     /*
  994.      * Select the button if the associated variable has the
  995.      * appropriate value, initialize the variable if it doesn't
  996.      * exist, then set a trace on the variable to monitor future
  997.      * changes to its value.
  998.      */
  999.  
  1000.     value = Tcl_GetVar(interp, butPtr->selVarName, TCL_GLOBAL_ONLY);
  1001.     butPtr->flags &= ~SELECTED;
  1002.     if (value != NULL) {
  1003.         if (strcmp(value, butPtr->onValue) == 0) {
  1004.         butPtr->flags |= SELECTED;
  1005.         }
  1006.     } else {
  1007.         if (Tcl_SetVar(interp, butPtr->selVarName,
  1008.             (butPtr->type == TYPE_CHECK_BUTTON) ? butPtr->offValue : "",
  1009.             TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  1010.         return TCL_ERROR;
  1011.         }
  1012.     }
  1013.     Tcl_TraceVar(interp, butPtr->selVarName,
  1014.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  1015.         ButtonVarProc, (ClientData) butPtr);
  1016.     }
  1017.  
  1018.     /*
  1019.      * Get the images for the widget, if there are any.  Allocate the
  1020.      * new images before freeing the old ones, so that the reference
  1021.      * counts don't go to zero and cause image data to be discarded.
  1022.      */
  1023.  
  1024.     if (butPtr->imageString != NULL) {
  1025.     image = Tk_GetImage(butPtr->interp, butPtr->tkwin,
  1026.         butPtr->imageString, ButtonImageProc, (ClientData) butPtr);
  1027.     if (image == NULL) {
  1028.         return TCL_ERROR;
  1029.     }
  1030.     } else {
  1031.     image = NULL;
  1032.     }
  1033.     if (butPtr->image != NULL) {
  1034.     Tk_FreeImage(butPtr->image);
  1035.     }
  1036.     butPtr->image = image;
  1037.     if (butPtr->selectImageString != NULL) {
  1038.     image = Tk_GetImage(butPtr->interp, butPtr->tkwin,
  1039.         butPtr->selectImageString, ButtonSelectImageProc,
  1040.         (ClientData) butPtr);
  1041.     if (image == NULL) {
  1042.         return TCL_ERROR;
  1043.     }
  1044.     } else {
  1045.     image = NULL;
  1046.     }
  1047.     if (butPtr->selectImage != NULL) {
  1048.     Tk_FreeImage(butPtr->selectImage);
  1049.     }
  1050.     butPtr->selectImage = image;
  1051.  
  1052.     if ((butPtr->image == NULL) && (butPtr->bitmap == None)
  1053.         && (butPtr->textVarName != NULL)) {
  1054.     /*
  1055.      * The button must display the value of a variable: set up a trace
  1056.      * on the variable's value, create the variable if it doesn't
  1057.      * exist, and fetch its current value.
  1058.      */
  1059.  
  1060.     char *value;
  1061.  
  1062.     value = Tcl_GetVar(interp, butPtr->textVarName, TCL_GLOBAL_ONLY);
  1063.     if (value == NULL) {
  1064.         if (Tcl_SetVar(interp, butPtr->textVarName, butPtr->text,
  1065.             TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  1066.         return TCL_ERROR;
  1067.         }
  1068.     } else {
  1069.         if (butPtr->text != NULL) {
  1070.         ckfree(butPtr->text);
  1071.         }
  1072.         butPtr->text = (char *) ckalloc((unsigned) (strlen(value) + 1));
  1073.         strcpy(butPtr->text, value);
  1074.     }
  1075.     Tcl_TraceVar(interp, butPtr->textVarName,
  1076.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  1077.         ButtonTextVarProc, (ClientData) butPtr);
  1078.     }
  1079.  
  1080.     if ((butPtr->bitmap != None) || (butPtr->image != NULL)) {
  1081.     if (Tk_GetPixels(interp, butPtr->tkwin, butPtr->widthString,
  1082.         &butPtr->width) != TCL_OK) {
  1083.         widthError:
  1084.         Tcl_AddErrorInfo(interp, "\n    (processing -width option)");
  1085.         return TCL_ERROR;
  1086.     }
  1087.     if (Tk_GetPixels(interp, butPtr->tkwin, butPtr->heightString,
  1088.         &butPtr->height) != TCL_OK) {
  1089.         heightError:
  1090.         Tcl_AddErrorInfo(interp, "\n    (processing -height option)");
  1091.         return TCL_ERROR;
  1092.     }
  1093.     } else {
  1094.     if (Tcl_GetInt(interp, butPtr->widthString, &butPtr->width)
  1095.         != TCL_OK) {
  1096.         goto widthError;
  1097.     }
  1098.     if (Tcl_GetInt(interp, butPtr->heightString, &butPtr->height)
  1099.         != TCL_OK) {
  1100.         goto heightError;
  1101.     }
  1102.     }
  1103.     ComputeButtonGeometry(butPtr);
  1104.  
  1105.     /*
  1106.      * Lastly, arrange for the button to be redisplayed.
  1107.      */
  1108.  
  1109.     if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) {
  1110.     Tcl_DoWhenIdle(DisplayButton, (ClientData) butPtr);
  1111.     butPtr->flags |= REDRAW_PENDING;
  1112.     }
  1113.  
  1114.     return TCL_OK;
  1115. }
  1116.  
  1117.  
  1118. /*
  1119.  *----------------------------------------------------------------------
  1120.  *
  1121.  * DisplayButton --
  1122.  *
  1123.  *    This procedure is invoked to display a button widget.  It is
  1124.  *    normally invoked as an idle handler.
  1125.  *
  1126.  * Results:
  1127.  *    None.
  1128.  *
  1129.  * Side effects:
  1130.  *    Commands are output to X to display the button in its
  1131.  *    current mode.  The REDRAW_PENDING flag is cleared.
  1132.  *
  1133.  *----------------------------------------------------------------------
  1134.  */
  1135.  
  1136. static void
  1137. DisplayButton(clientData)
  1138.     ClientData clientData;    /* Information about widget. */
  1139. {
  1140.     register Button *butPtr = (Button *) clientData;
  1141.     GC gc;
  1142.     Tk_3DBorder border;
  1143.     Pixmap pixmap;
  1144.     int x = 0;            /* Initialization only needed to stop
  1145.                  * compiler warning. */
  1146.     int y, relief;
  1147.     register Tk_Window tkwin = butPtr->tkwin;
  1148.     int width, height;
  1149.     int offset;            /* 0 means this is a label widget.  1 means
  1150.                  * it is a flavor of button, so we offset
  1151.                  * the text to make the button appear to
  1152.                  * move up and down as the relief changes. */
  1153.  
  1154.     butPtr->flags &= ~REDRAW_PENDING;
  1155.     if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  1156.     return;
  1157.     }
  1158.  
  1159.     border = butPtr->normalBorder;
  1160.     if ((butPtr->state == tkDisabledUid) && (butPtr->disabledFg != NULL)) {
  1161.     gc = butPtr->disabledGC;
  1162.     } else if ((butPtr->state == tkActiveUid)
  1163.         && !Tk_StrictMotif(butPtr->tkwin)) {
  1164.     gc = butPtr->activeTextGC;
  1165.     border = butPtr->activeBorder;
  1166.     } else {
  1167.     gc = butPtr->normalTextGC;
  1168.     }
  1169.     if ((butPtr->flags & SELECTED) && (butPtr->state != tkActiveUid)
  1170.         && (butPtr->selectBorder != NULL) && !butPtr->indicatorOn) {
  1171.     border = butPtr->selectBorder;
  1172.     }
  1173.  
  1174.     /*
  1175.      * Override the relief specified for the button if this is a
  1176.      * checkbutton or radiobutton and there's no indicator.
  1177.      */
  1178.  
  1179.     relief = butPtr->relief;
  1180.     if ((butPtr->type >= TYPE_CHECK_BUTTON) && !butPtr->indicatorOn) {
  1181.     relief = (butPtr->flags & SELECTED) ? TK_RELIEF_SUNKEN
  1182.         : TK_RELIEF_RAISED;
  1183.     }
  1184.  
  1185.     offset = (butPtr->type == TYPE_BUTTON) && !Tk_StrictMotif(butPtr->tkwin);
  1186.  
  1187.     /*
  1188.      * In order to avoid screen flashes, this procedure redraws
  1189.      * the button in a pixmap, then copies the pixmap to the
  1190.      * screen in a single operation.  This means that there's no
  1191.      * point in time where the on-sreen image has been cleared.
  1192.      */
  1193.  
  1194.     pixmap = Tk_GetPixmap(butPtr->display, Tk_WindowId(tkwin),
  1195.         Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
  1196.     Tk_Fill3DRectangle(tkwin, pixmap, border, 0, 0, Tk_Width(tkwin),
  1197.         Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
  1198.  
  1199.     /*
  1200.      * Display image or bitmap or text for button.
  1201.      */
  1202.  
  1203.     if (butPtr->image != None) {
  1204.     Tk_SizeOfImage(butPtr->image, &width, &height);
  1205.  
  1206.     imageOrBitmap:
  1207.     switch (butPtr->anchor) {
  1208.         case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW:
  1209.         x = butPtr->inset + butPtr->indicatorSpace + offset;
  1210.         break;
  1211.         case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S:
  1212.         x = ((int) (Tk_Width(tkwin) + butPtr->indicatorSpace
  1213.             - width))/2;
  1214.         break;
  1215.         default:
  1216.         x = Tk_Width(tkwin) - butPtr->inset - width - offset;
  1217.         break;
  1218.     }
  1219.     switch (butPtr->anchor) {
  1220.         case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE:
  1221.         y = butPtr->inset + offset;
  1222.         break;
  1223.         case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
  1224.         y = ((int) (Tk_Height(tkwin) - height))/2;
  1225.         break;
  1226.         default:
  1227.         y = Tk_Height(tkwin) - butPtr->inset - height - offset;
  1228.         break;
  1229.     }
  1230.     if (relief == TK_RELIEF_RAISED) {
  1231.         x -= offset;
  1232.         y -= offset;
  1233.     } else if (relief == TK_RELIEF_SUNKEN) {
  1234.         x += offset;
  1235.         y += offset;
  1236.     }
  1237.     if (butPtr->image != NULL) {
  1238.         if ((butPtr->selectImage != NULL) && (butPtr->flags & SELECTED)) {
  1239.         Tk_RedrawImage(butPtr->selectImage, 0, 0, width, height, pixmap,
  1240.             x, y);
  1241.         } else {
  1242.         Tk_RedrawImage(butPtr->image, 0, 0, width, height, pixmap,
  1243.             x, y);
  1244.         }
  1245.     } else {
  1246.         XSetClipOrigin(butPtr->display, gc, x, y);
  1247.         XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc, 0, 0,
  1248.             (unsigned int) width, (unsigned int) height, x, y, 1);
  1249.         XSetClipOrigin(butPtr->display, gc, 0, 0);
  1250.     }
  1251.     y += height/2;
  1252.     } else if (butPtr->bitmap != None) {
  1253.     Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
  1254.     goto imageOrBitmap;
  1255.     } else {
  1256.     switch (butPtr->anchor) {
  1257.         case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW:
  1258.         x = butPtr->inset + butPtr->padX + butPtr->indicatorSpace
  1259.             + offset;
  1260.         break;
  1261.         case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S:
  1262.         x = ((int) (Tk_Width(tkwin) + butPtr->indicatorSpace
  1263.             - butPtr->textWidth))/2;
  1264.         break;
  1265.         default:
  1266.         x = Tk_Width(tkwin) - butPtr->inset - butPtr->padX
  1267.             - butPtr->textWidth - offset;
  1268.         break;
  1269.     }
  1270.     switch (butPtr->anchor) {
  1271.         case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE:
  1272.         y = butPtr->inset + butPtr->padY + offset;
  1273.         break;
  1274.         case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
  1275.         y = ((int) (Tk_Height(tkwin) - butPtr->textHeight))/2;
  1276.         break;
  1277.         default:
  1278.         y = Tk_Height(tkwin) - butPtr->inset - butPtr->padY
  1279.             - butPtr->textHeight - offset;
  1280.         break;
  1281.     }
  1282.     if (relief == TK_RELIEF_RAISED) {
  1283.         x -= offset;
  1284.         y -= offset;
  1285.     } else if (relief == TK_RELIEF_SUNKEN) {
  1286.         x += offset;
  1287.         y += offset;
  1288.     }
  1289.     TkDisplayText(butPtr->display, pixmap, butPtr->fontPtr,
  1290.         butPtr->text, butPtr->numChars, x, y, butPtr->textWidth,
  1291.         butPtr->justify, butPtr->underline, gc);
  1292.     y += butPtr->textHeight/2;
  1293.     }
  1294.  
  1295.     /*
  1296.      * Draw the indicator for check buttons and radio buttons.  At this
  1297.      * point x and y refer to the top-left corner of the text or image
  1298.      * or bitmap.
  1299.      */
  1300.  
  1301.     if ((butPtr->type == TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
  1302.     int dim;
  1303.  
  1304.     dim = butPtr->indicatorDiameter;
  1305.     x -= butPtr->indicatorSpace;
  1306.     y -= dim/2;
  1307.     if (dim > 2*butPtr->borderWidth) {
  1308.         Tk_Draw3DRectangle(tkwin, pixmap, border, x, y, dim, dim,
  1309.             butPtr->borderWidth, TK_RELIEF_RAISED);
  1310.         x += butPtr->borderWidth;
  1311.         y += butPtr->borderWidth;
  1312.         dim -= 2*butPtr->borderWidth;
  1313.         if (butPtr->flags & SELECTED) {
  1314.         Tk_DrawCheckMark(butPtr->display, tkwin, pixmap, 
  1315.             x+1+(dim-CHECK_WIDTH)/2, y+(dim-CHECK_HEIGHT)/2,
  1316.             butPtr->normalBorder);
  1317.         } else {
  1318.         Tk_Fill3DRectangle(tkwin, pixmap, butPtr->normalBorder, x, y,
  1319.             dim, dim, butPtr->borderWidth, TK_RELIEF_FLAT);
  1320.         }
  1321.     }
  1322.     } else if ((butPtr->type == TYPE_RADIO_BUTTON) && butPtr->indicatorOn) {
  1323.     int radius;
  1324.     GC gc;
  1325.     
  1326.     radius = butPtr->indicatorDiameter/2;
  1327.     if (butPtr->flags & SELECTED) {
  1328.         gc = Tk_3DBorderGC(tkwin, (butPtr->selectBorder != NULL)
  1329.             ? butPtr->selectBorder : butPtr->normalBorder,
  1330.             TK_3D_FLAT_GC);
  1331.         XFillArc(butPtr->display, pixmap, gc, x-butPtr->indicatorSpace+
  1332.              butPtr->borderWidth, butPtr->borderWidth + y-radius,
  1333.              radius*2-butPtr->borderWidth-1,
  1334.              radius*2-butPtr->borderWidth-1,0,360*64);
  1335.     } else {
  1336.         gc = Tk_3DBorderGC(tkwin, butPtr->normalBorder, TK_3D_FLAT_GC);
  1337.         XFillArc(butPtr->display, pixmap, gc, x-butPtr->indicatorSpace,
  1338.              y-radius,  radius*2-butPtr->borderWidth-1,
  1339.              radius*2-butPtr->borderWidth-1,0,360*64);
  1340.     }
  1341.     Tk_Draw3DCircle(butPtr->display, tkwin,pixmap,x-butPtr->indicatorSpace,
  1342.          y-radius, butPtr->borderWidth, radius, TK_RELIEF_SUNKEN, 
  1343.          butPtr->normalBorder);
  1344.     }
  1345.     /*
  1346.      * If the button is disabled with a stipple rather than a special
  1347.      * foreground color, generate the stippled effect.  If the widget
  1348.      * is selected and we use a different background color when selected,
  1349.      * must temporarily modify the GC.
  1350.      */
  1351.  
  1352.     if ((butPtr->state == tkDisabledUid)
  1353.         && ((butPtr->disabledFg == NULL) || (butPtr->image != NULL))) {
  1354.     if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn
  1355.         && (butPtr->selectBorder != NULL)) {
  1356.         XSetForeground(butPtr->display, butPtr->disabledGC,
  1357.             Tk_3DBorderColor(butPtr->selectBorder)->pixel);
  1358.     }
  1359.     XFillRectangle(butPtr->display, pixmap, butPtr->disabledGC,
  1360.         butPtr->inset, butPtr->inset,
  1361.         (unsigned) (Tk_Width(tkwin) - 2*butPtr->inset),
  1362.         (unsigned) (Tk_Height(tkwin) - 2*butPtr->inset));
  1363.     if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn
  1364.         && (butPtr->selectBorder != NULL)) {
  1365.         XSetForeground(butPtr->display, butPtr->disabledGC,
  1366.             Tk_3DBorderColor(butPtr->normalBorder)->pixel);
  1367.     }
  1368.     }
  1369.  
  1370.     /*
  1371.      * Draw the border and traversal highlight last.  This way, if the
  1372.      * button's contents overflow they'll be covered up by the border.
  1373.      */
  1374.  
  1375.     if (relief != TK_RELIEF_FLAT) {
  1376.     Tk_Draw3DRectangle(tkwin, pixmap, border,
  1377.         butPtr->highlightWidth, butPtr->highlightWidth,
  1378.         Tk_Width(tkwin) - 2*butPtr->highlightWidth,
  1379.         Tk_Height(tkwin) - 2*butPtr->highlightWidth,
  1380.         butPtr->borderWidth, relief);
  1381.     }
  1382.     if (butPtr->highlightWidth != 0) {
  1383.     GC gc;
  1384.  
  1385.     if (butPtr->flags & GOT_FOCUS) {
  1386.         gc = Tk_GCForColor(butPtr->highlightColorPtr, pixmap);
  1387.     } else {
  1388.         gc = Tk_GCForColor(butPtr->highlightBgColorPtr, pixmap);
  1389.     }
  1390.     Tk_DrawFocusHighlight(tkwin, gc, butPtr->highlightWidth, pixmap);
  1391.     }
  1392.  
  1393.     /*
  1394.      * Copy the information from the off-screen pixmap onto the screen,
  1395.      * then delete the pixmap.
  1396.      */
  1397.  
  1398.     XCopyArea(butPtr->display, pixmap, Tk_WindowId(tkwin),
  1399.         butPtr->copyGC, 0, 0, (unsigned) Tk_Width(tkwin),
  1400.         (unsigned) Tk_Height(tkwin), 0, 0);
  1401.     Tk_FreePixmap(butPtr->display, pixmap);
  1402. }
  1403.  
  1404. /*
  1405.  *--------------------------------------------------------------
  1406.  *
  1407.  * ButtonEventProc --
  1408.  *
  1409.  *    This procedure is invoked by the Tk dispatcher for various
  1410.  *    events on buttons.
  1411.  *
  1412.  * Results:
  1413.  *    None.
  1414.  *
  1415.  * Side effects:
  1416.  *    When the window gets deleted, internal structures get
  1417.  *    cleaned up.  When it gets exposed, it is redisplayed.
  1418.  *
  1419.  *--------------------------------------------------------------
  1420.  */
  1421.  
  1422. static void
  1423. ButtonEventProc(clientData, eventPtr)
  1424.     ClientData clientData;    /* Information about window. */
  1425.     XEvent *eventPtr;        /* Information about event. */
  1426. {
  1427.     Button *butPtr = (Button *) clientData;
  1428.     if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
  1429.     goto redraw;
  1430.     } else if (eventPtr->type == ConfigureNotify) {
  1431.     /*
  1432.      * Must redraw after size changes, since layout could have changed
  1433.      * and borders will need to be redrawn.
  1434.      */
  1435.  
  1436.     goto redraw;
  1437.     } else if (eventPtr->type == DestroyNotify) {
  1438.     if (butPtr->tkwin != NULL) {
  1439.         butPtr->tkwin = NULL;
  1440.         Tcl_DeleteCommand(butPtr->interp,
  1441.             Tcl_GetCommandName(butPtr->interp, butPtr->widgetCmd));
  1442.     }
  1443.     if (butPtr->flags & REDRAW_PENDING) {
  1444.         Tcl_CancelIdleCall(DisplayButton, (ClientData) butPtr);
  1445.     }
  1446.     DestroyButton(butPtr);
  1447.     } else if (eventPtr->type == FocusIn) {
  1448.     if (eventPtr->xfocus.detail != NotifyInferior) {
  1449.         butPtr->flags |= GOT_FOCUS;
  1450.         if (butPtr->highlightWidth > 0) {
  1451.         goto redraw;
  1452.         }
  1453.     }
  1454.     } else if (eventPtr->type == FocusOut) {
  1455.     if (eventPtr->xfocus.detail != NotifyInferior) {
  1456.         butPtr->flags &= ~GOT_FOCUS;
  1457.         if (butPtr->highlightWidth > 0) {
  1458.         goto redraw;
  1459.         }
  1460.     }
  1461.     }
  1462.     return;
  1463.  
  1464.     redraw:
  1465.     if ((butPtr->tkwin != NULL) && !(butPtr->flags & REDRAW_PENDING)) {
  1466.     Tcl_DoWhenIdle(DisplayButton, (ClientData) butPtr);
  1467.     butPtr->flags |= REDRAW_PENDING;
  1468.     }
  1469. }
  1470.  
  1471. /*
  1472.  *----------------------------------------------------------------------
  1473.  *
  1474.  * ButtonCmdDeletedProc --
  1475.  *
  1476.  *    This procedure is invoked when a widget command is deleted.  If
  1477.  *    the widget isn't already in the process of being destroyed,
  1478.  *    this command destroys it.
  1479.  *
  1480.  * Results:
  1481.  *    None.
  1482.  *
  1483.  * Side effects:
  1484.  *    The widget is destroyed.
  1485.  *
  1486.  *----------------------------------------------------------------------
  1487.  */
  1488.  
  1489. static void
  1490. ButtonCmdDeletedProc(clientData)
  1491.     ClientData clientData;    /* Pointer to widget record for widget. */
  1492. {
  1493.     Button *butPtr = (Button *) clientData;
  1494.     Tk_Window tkwin = butPtr->tkwin;
  1495.  
  1496.     /*
  1497.      * This procedure could be invoked either because the window was
  1498.      * destroyed and the command was then deleted (in which case tkwin
  1499.      * is NULL) or because the command was deleted, and then this procedure
  1500.      * destroys the widget.
  1501.      */
  1502.  
  1503.     if (tkwin != NULL) {
  1504.     butPtr->tkwin = NULL;
  1505.     Tk_DestroyWindow(tkwin);
  1506.     }
  1507. }
  1508.  
  1509. /*
  1510.  *----------------------------------------------------------------------
  1511.  *
  1512.  * ComputeButtonGeometry --
  1513.  *
  1514.  *    After changes in a button's text or bitmap, this procedure
  1515.  *    recomputes the button's geometry and passes this information
  1516.  *    along to the geometry manager for the window.
  1517.  *
  1518.  * Results:
  1519.  *    None.
  1520.  *
  1521.  * Side effects:
  1522.  *    The button's window may change size.
  1523.  *
  1524.  *----------------------------------------------------------------------
  1525.  */
  1526.  
  1527. static void
  1528. ComputeButtonGeometry(butPtr)
  1529.     register Button *butPtr;    /* Button whose geometry may have changed. */
  1530. {
  1531.     int width, height;
  1532.  
  1533.     if (butPtr->highlightWidth < 0) {
  1534.     butPtr->highlightWidth = 0;
  1535.     }
  1536.     butPtr->inset = butPtr->highlightWidth + butPtr->borderWidth;
  1537.     butPtr->indicatorSpace = 0;
  1538.     if (butPtr->image != NULL) {
  1539.     Tk_SizeOfImage(butPtr->image, &width, &height);
  1540.     imageOrBitmap:
  1541.     if (butPtr->width > 0) {
  1542.         width = butPtr->width;
  1543.     }
  1544.     if (butPtr->height > 0) {
  1545.         height = butPtr->height;
  1546.     }
  1547.     if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
  1548.         butPtr->indicatorSpace = height;
  1549.         if (butPtr->type == TYPE_CHECK_BUTTON) {
  1550.         butPtr->indicatorDiameter = (65*height)/100;
  1551.         } else {
  1552.         butPtr->indicatorDiameter = (75*height)/100;
  1553.         }
  1554.     }
  1555.     } else if (butPtr->bitmap != None) {
  1556.     Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
  1557.     goto imageOrBitmap;
  1558.     } else {
  1559.     butPtr->numChars = strlen(butPtr->text);
  1560.     TkComputeTextGeometry(butPtr->fontPtr, butPtr->text,
  1561.         butPtr->numChars, butPtr->wrapLength, &butPtr->textWidth,
  1562.         &butPtr->textHeight);
  1563.     width = butPtr->textWidth;
  1564.     height = butPtr->textHeight;
  1565.     if (butPtr->width > 0) {
  1566.         width = butPtr->width * XTextWidth(butPtr->fontPtr, "0", 1);
  1567.     }
  1568.     if (butPtr->height > 0) {
  1569.         height = butPtr->height * (butPtr->fontPtr->ascent
  1570.             + butPtr->fontPtr->descent);
  1571.     }
  1572.     if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
  1573.         butPtr->indicatorDiameter = butPtr->fontPtr->ascent
  1574.             + butPtr->fontPtr->descent;
  1575.         if (butPtr->type == TYPE_CHECK_BUTTON) {
  1576.         butPtr->indicatorDiameter++;
  1577.         }
  1578.         butPtr->indicatorSpace = butPtr->indicatorDiameter
  1579.         + XTextWidth(butPtr->fontPtr, "0", 1);
  1580.     }
  1581.     }
  1582.  
  1583.     /*
  1584.      * When issuing the geometry request, add extra space for the indicator,
  1585.      * if any, and for the border and padding, plus two extra pixels so the
  1586.      * display can be offset by 1 pixel in either direction for the raised
  1587.      * or lowered effect.
  1588.      */
  1589.  
  1590.     if ((butPtr->image == NULL) && (butPtr->bitmap == None)) {
  1591.     width += 2*butPtr->padX;
  1592.     height += 2*butPtr->padY;
  1593.     }
  1594.     if ((butPtr->type == TYPE_BUTTON) && !Tk_StrictMotif(butPtr->tkwin)) {
  1595.     width += 2;
  1596.     height += 2;
  1597.     }
  1598.     Tk_GeometryRequest(butPtr->tkwin, (int) (width + butPtr->indicatorSpace
  1599.         + 2*butPtr->inset), (int) (height + 2*butPtr->inset));
  1600.     Tk_SetInternalBorder(butPtr->tkwin, butPtr->inset);
  1601. }
  1602.  
  1603. /*
  1604.  *----------------------------------------------------------------------
  1605.  *
  1606.  * InvokeButton --
  1607.  *
  1608.  *    This procedure is called to carry out the actions associated
  1609.  *    with a button, such as invoking a Tcl command or setting a
  1610.  *    variable.  This procedure is invoked, for example, when the
  1611.  *    button is invoked via the mouse.
  1612.  *
  1613.  * Results:
  1614.  *    A standard Tcl return value.  Information is also left in
  1615.  *    interp->result.
  1616.  *
  1617.  * Side effects:
  1618.  *    Depends on the button and its associated command.
  1619.  *
  1620.  *----------------------------------------------------------------------
  1621.  */
  1622.  
  1623. static int
  1624. InvokeButton(butPtr)
  1625.     register Button *butPtr;        /* Information about button. */
  1626. {
  1627.     if (butPtr->type == TYPE_CHECK_BUTTON) {
  1628.     if (butPtr->flags & SELECTED) {
  1629.         if (Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->offValue,
  1630.             TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  1631.         return TCL_ERROR;
  1632.         }
  1633.     } else {
  1634.         if (Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->onValue,
  1635.             TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  1636.         return TCL_ERROR;
  1637.         }
  1638.     }
  1639.     } else if (butPtr->type == TYPE_RADIO_BUTTON) {
  1640.     if (Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->onValue,
  1641.         TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  1642.         return TCL_ERROR;
  1643.     }
  1644.     }
  1645.     if ((butPtr->type != TYPE_LABEL) && (butPtr->command != NULL)) {
  1646.     return TkCopyAndGlobalEval(butPtr->interp, butPtr->command);
  1647.     }
  1648.     return TCL_OK;
  1649. }
  1650.  
  1651. /*
  1652.  *--------------------------------------------------------------
  1653.  *
  1654.  * ButtonVarProc --
  1655.  *
  1656.  *    This procedure is invoked when someone changes the
  1657.  *    state variable associated with a radio button.  Depending
  1658.  *    on the new value of the button's variable, the button
  1659.  *    may be selected or deselected.
  1660.  *
  1661.  * Results:
  1662.  *    NULL is always returned.
  1663.  *
  1664.  * Side effects:
  1665.  *    The button may become selected or deselected.
  1666.  *
  1667.  *--------------------------------------------------------------
  1668.  */
  1669.  
  1670.     /* ARGSUSED */
  1671. static char *
  1672. ButtonVarProc(clientData, interp, name1, name2, flags)
  1673.     ClientData clientData;    /* Information about button. */
  1674.     Tcl_Interp *interp;        /* Interpreter containing variable. */
  1675.     char *name1;        /* Name of variable. */
  1676.     char *name2;        /* Second part of variable name. */
  1677.     int flags;            /* Information about what happened. */
  1678. {
  1679.     register Button *butPtr = (Button *) clientData;
  1680.     char *value;
  1681.  
  1682.     /*
  1683.      * If the variable is being unset, then just re-establish the
  1684.      * trace unless the whole interpreter is going away.
  1685.      */
  1686.  
  1687.     if (flags & TCL_TRACE_UNSETS) {
  1688.     butPtr->flags &= ~SELECTED;
  1689.     if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
  1690.         Tcl_TraceVar(interp, butPtr->selVarName,
  1691.             TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  1692.             ButtonVarProc, clientData);
  1693.     }
  1694.     goto redisplay;
  1695.     }
  1696.  
  1697.     /*
  1698.      * Use the value of the variable to update the selected status of
  1699.      * the button.
  1700.      */
  1701.  
  1702.     value = Tcl_GetVar(interp, butPtr->selVarName, TCL_GLOBAL_ONLY);
  1703.     if (value == NULL) {
  1704.     value = "";
  1705.     }
  1706.     if (strcmp(value, butPtr->onValue) == 0) {
  1707.     if (butPtr->flags & SELECTED) {
  1708.         return (char *) NULL;
  1709.     }
  1710.     butPtr->flags |= SELECTED;
  1711.     } else if (butPtr->flags & SELECTED) {
  1712.     butPtr->flags &= ~SELECTED;
  1713.     } else {
  1714.     return (char *) NULL;
  1715.     }
  1716.  
  1717.     redisplay:
  1718.     if ((butPtr->tkwin != NULL) && Tk_IsMapped(butPtr->tkwin)
  1719.         && !(butPtr->flags & REDRAW_PENDING)) {
  1720.     Tcl_DoWhenIdle(DisplayButton, (ClientData) butPtr);
  1721.     butPtr->flags |= REDRAW_PENDING;
  1722.     }
  1723.     return (char *) NULL;
  1724. }
  1725.  
  1726. /*
  1727.  *--------------------------------------------------------------
  1728.  *
  1729.  * ButtonTextVarProc --
  1730.  *
  1731.  *    This procedure is invoked when someone changes the variable
  1732.  *    whose contents are to be displayed in a button.
  1733.  *
  1734.  * Results:
  1735.  *    NULL is always returned.
  1736.  *
  1737.  * Side effects:
  1738.  *    The text displayed in the button will change to match the
  1739.  *    variable.
  1740.  *
  1741.  *--------------------------------------------------------------
  1742.  */
  1743.  
  1744.     /* ARGSUSED */
  1745. static char *
  1746. ButtonTextVarProc(clientData, interp, name1, name2, flags)
  1747.     ClientData clientData;    /* Information about button. */
  1748.     Tcl_Interp *interp;        /* Interpreter containing variable. */
  1749.     char *name1;        /* Not used. */
  1750.     char *name2;        /* Not used. */
  1751.     int flags;            /* Information about what happened. */
  1752. {
  1753.     register Button *butPtr = (Button *) clientData;
  1754.     char *value;
  1755.  
  1756.     /*
  1757.      * If the variable is unset, then immediately recreate it unless
  1758.      * the whole interpreter is going away.
  1759.      */
  1760.  
  1761.     if (flags & TCL_TRACE_UNSETS) {
  1762.     if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
  1763.         Tcl_SetVar(interp, butPtr->textVarName, butPtr->text,
  1764.             TCL_GLOBAL_ONLY);
  1765.         Tcl_TraceVar(interp, butPtr->textVarName,
  1766.             TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  1767.             ButtonTextVarProc, clientData);
  1768.     }
  1769.     return (char *) NULL;
  1770.     }
  1771.  
  1772.     value = Tcl_GetVar(interp, butPtr->textVarName, TCL_GLOBAL_ONLY);
  1773.     if (value == NULL) {
  1774.     value = "";
  1775.     }
  1776.     if (butPtr->text != NULL) {
  1777.     ckfree(butPtr->text);
  1778.     }
  1779.     butPtr->text = (char *) ckalloc((unsigned) (strlen(value) + 1));
  1780.     strcpy(butPtr->text, value);
  1781.     ComputeButtonGeometry(butPtr);
  1782.  
  1783.     if ((butPtr->tkwin != NULL) && Tk_IsMapped(butPtr->tkwin)
  1784.         && !(butPtr->flags & REDRAW_PENDING)) {
  1785.     Tcl_DoWhenIdle(DisplayButton, (ClientData) butPtr);
  1786.     butPtr->flags |= REDRAW_PENDING;
  1787.     }
  1788.     return (char *) NULL;
  1789. }
  1790.  
  1791. /*
  1792.  *----------------------------------------------------------------------
  1793.  *
  1794.  * ButtonImageProc --
  1795.  *
  1796.  *    This procedure is invoked by the image code whenever the manager
  1797.  *    for an image does something that affects the size of contents
  1798.  *    of an image displayed in a button.
  1799.  *
  1800.  * Results:
  1801.  *    None.
  1802.  *
  1803.  * Side effects:
  1804.  *    Arranges for the button to get redisplayed.
  1805.  *
  1806.  *----------------------------------------------------------------------
  1807.  */
  1808.  
  1809. static void
  1810. ButtonImageProc(clientData, x, y, width, height, imgWidth, imgHeight)
  1811.     ClientData clientData;        /* Pointer to widget record. */
  1812.     int x, y;                /* Upper left pixel (within image)
  1813.                      * that must be redisplayed. */
  1814.     int width, height;            /* Dimensions of area to redisplay
  1815.                      * (may be <= 0). */
  1816.     int imgWidth, imgHeight;        /* New dimensions of image. */
  1817. {
  1818.     register Button *butPtr = (Button *) clientData;
  1819.  
  1820.     if (butPtr->tkwin != NULL) {
  1821.     ComputeButtonGeometry(butPtr);
  1822.     if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) {
  1823.         Tcl_DoWhenIdle(DisplayButton, (ClientData) butPtr);
  1824.         butPtr->flags |= REDRAW_PENDING;
  1825.     }
  1826.     }
  1827. }
  1828.  
  1829. /*
  1830.  *----------------------------------------------------------------------
  1831.  *
  1832.  * ButtonSelectImageProc --
  1833.  *
  1834.  *    This procedure is invoked by the image code whenever the manager
  1835.  *    for an image does something that affects the size of contents
  1836.  *    of the image displayed in a button when it is selected.
  1837.  *
  1838.  * Results:
  1839.  *    None.
  1840.  *
  1841.  * Side effects:
  1842.  *    May arrange for the button to get redisplayed.
  1843.  *
  1844.  *----------------------------------------------------------------------
  1845.  */
  1846.  
  1847. static void
  1848. ButtonSelectImageProc(clientData, x, y, width, height, imgWidth, imgHeight)
  1849.     ClientData clientData;        /* Pointer to widget record. */
  1850.     int x, y;                /* Upper left pixel (within image)
  1851.                      * that must be redisplayed. */
  1852.     int width, height;            /* Dimensions of area to redisplay
  1853.                      * (may be <= 0). */
  1854.     int imgWidth, imgHeight;        /* New dimensions of image. */
  1855. {
  1856.     register Button *butPtr = (Button *) clientData;
  1857.  
  1858.     /*
  1859.      * Don't recompute geometry:  it's controlled by the primary image.
  1860.      */
  1861.  
  1862.     if ((butPtr->flags & SELECTED) && (butPtr->tkwin != NULL)
  1863.         && Tk_IsMapped(butPtr->tkwin)
  1864.         && !(butPtr->flags & REDRAW_PENDING)) {
  1865.     Tcl_DoWhenIdle(DisplayButton, (ClientData) butPtr);
  1866.     butPtr->flags |= REDRAW_PENDING;
  1867.     }
  1868. }
  1869.